package cn.yx.common.security.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.yx.common.core.constant.SeparatorConstant;
import cn.yx.common.core.util.date.DateUtil;
import cn.yx.common.redis.service.RedisService;
import cn.yx.common.security.constants.SecurityRedisCacheConstant;
import cn.yx.common.security.pojo.JwtCacheToken;
import cn.yx.common.security.pojo.JwtModifyUserDetails;
import cn.yx.common.security.pojo.JwtUserDetails;
import cn.yx.common.security.properties.SecurityProperties;
import cn.yx.common.security.util.JwtUtil;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * <p>JwtToken服务类</p>
 *
 * @author Wgssmart
 */
@Component
public class JwtTokenService {

    private final RedisService redisService;

    private final SecurityProperties securityProperties;

    public JwtTokenService(SecurityProperties securityProperties, RedisService redisService) {
        this.redisService = redisService;
        this.securityProperties = securityProperties;
    }

    /**
     * 生成Jwt Token
     *
     * @param jwtUserDetails
     * @return
     */
    public String generateToken(JwtUserDetails jwtUserDetails) {
        return saveToken(jwtUserDetails);
    }

    /**
     * 刷新Jwt Token，可用于用户信息被更新时调用
     *
     * @param jwtUserDetails
     * @param jwtModifyUserDetails
     * @return
     */
    public String refreshToken(JwtUserDetails jwtUserDetails, JwtModifyUserDetails jwtModifyUserDetails) {
        JwtUserDetails newJwtUserDetails = new JwtUserDetails();
        newJwtUserDetails.setId(jwtUserDetails.getId());
        newJwtUserDetails.setAccount(StrUtil.isEmpty(jwtModifyUserDetails.getAccount()) ?
                jwtUserDetails.getAccount() : jwtModifyUserDetails.getAccount());
        newJwtUserDetails.setPhone(StrUtil.isEmpty(jwtModifyUserDetails.getPhone()) ?
                jwtUserDetails.getPhone() : jwtModifyUserDetails.getPhone());
        newJwtUserDetails.setEmail(StrUtil.isEmpty(jwtModifyUserDetails.getEmail()) ?
                jwtUserDetails.getEmail() : jwtModifyUserDetails.getEmail());
        newJwtUserDetails.setNickname(StrUtil.isEmpty(jwtModifyUserDetails.getNickname()) ?
                jwtUserDetails.getNickname() : jwtModifyUserDetails.getNickname());
        newJwtUserDetails.setAvatar(StrUtil.isEmpty(jwtModifyUserDetails.getAvatar()) ?
                jwtUserDetails.getAvatar() : jwtModifyUserDetails.getAvatar());
        newJwtUserDetails.setAuthorities(refreshAuthorities(jwtUserDetails, jwtModifyUserDetails));
        newJwtUserDetails.setEnabled(jwtUserDetails.isEnabled());
        newJwtUserDetails.setAccountNonExpired(jwtUserDetails.isAccountNonExpired());
        newJwtUserDetails.setAccountNonLocked(jwtUserDetails.isAccountNonLocked());
        newJwtUserDetails.setCredentialsNonExpired(jwtUserDetails.isCredentialsNonExpired());
        newJwtUserDetails.setTokenCreateTime(DateUtil.date());
        return saveToken(newJwtUserDetails);
    }

    /**
     * 保存jwt token到Redis，key规则：前缀加userId加_符号加Token创建时间的毫秒值
     *
     * @param jwtUserDetails
     * @return
     */
    private String saveToken(JwtUserDetails jwtUserDetails) {
        String token = JwtUtil.generateToken(jwtUserDetails, securityProperties.getJwtToken().getExpiration());
        // 支持用户多次登录，生成多个jwt token
        Date tokenCreateTime = jwtUserDetails.getTokenCreateTime();
        redisService.set(SecurityRedisCacheConstant.JWT_TOKEN_PREFIX + jwtUserDetails.getId()
                        + SeparatorConstant.UNDERLINE + tokenCreateTime.getTime(),
                new JwtCacheToken(token, tokenCreateTime),
                securityProperties.getJwtToken().getCacheExpiration(), TimeUnit.HOURS);
        return token;
    }

    private Collection<SimpleGrantedAuthority> refreshAuthorities(JwtUserDetails jwtUserDetails, JwtModifyUserDetails jwtModifyUserDetails) {
        Collection<SimpleGrantedAuthority> authorities = jwtUserDetails.getAuthorities();
        if (CollectionUtil.isNotEmpty(jwtModifyUserDetails.getAddRoles())) {
            for (String role : jwtModifyUserDetails.getAddRoles()) {
                authorities.add(new SimpleGrantedAuthority(role));
            }
        }
        if (CollectionUtil.isNotEmpty(jwtModifyUserDetails.getRemoveRoles())) {
            for (String role : jwtModifyUserDetails.getRemoveRoles()) {
                authorities.remove(new SimpleGrantedAuthority(role));
            }
        }
        return authorities;
    }

    /**
     * 获取Redis中缓存的jwt token
     *
     * @param jwtUserDetails
     * @return
     */
    public JwtCacheToken getCachedToken(JwtUserDetails jwtUserDetails) {
        String tokenKey = SecurityRedisCacheConstant.JWT_TOKEN_PREFIX + jwtUserDetails.getId()
                + SeparatorConstant.UNDERLINE + jwtUserDetails.getTokenCreateTime().getTime();
        Object cachedToken = redisService.get(tokenKey);
        return Objects.isNull(cachedToken) ? null : (JwtCacheToken) cachedToken;
    }

    /**
     * 刷新Redis中jwt token的缓存失效时间
     *
     * @param jwtUserDetails
     */
    public void refreshTokenExpiration(JwtUserDetails jwtUserDetails) {
        String tokenKey = SecurityRedisCacheConstant.JWT_TOKEN_PREFIX + jwtUserDetails.getId()
                + SeparatorConstant.UNDERLINE + jwtUserDetails.getTokenCreateTime().getTime();
        redisService.expire(tokenKey, securityProperties.getJwtToken().getCacheExpiration(), TimeUnit.HOURS);
    }

    /**
     * 从Redis中删除jwt token缓存
     *
     * @param jwtUserDetails
     */
    public void removeToken(JwtUserDetails jwtUserDetails) {
        String tokenKey = SecurityRedisCacheConstant.JWT_TOKEN_PREFIX + jwtUserDetails.getId()
                + SeparatorConstant.UNDERLINE + jwtUserDetails.getTokenCreateTime().getTime();
        redisService.delete(tokenKey);
    }

}
